﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using PGMHelper;
using System.Text.RegularExpressions;

namespace FiberVisible
{
    /// <summary>
    /// 截面信息
    /// </summary>
    public partial class SectionInfo
    {
        #region Variables

        /// <summary>
        /// 截面编号
        /// </summary>
        public string SectionName { set; get; }

        /// <summary>
        /// 纤维列表
        /// </summary>
        public List<FiberInfo> FiberList { set; get; }

        /// <summary>
        /// 主纤维编号
        /// </summary>
        public List<FiberGroup> FiberGroupInfoList { set; get; }

        /// <summary>
        /// 原始纤维信息
        /// </summary>
        public List<string> OriFiberInfoList { set; get; }

        /// <summary>
        /// 纤维颜色列表
        /// </summary>
        private List<Color> ColorList { set; get; }

        /// <summary>
        /// 估计截面H高度
        /// 用于绘图
        /// </summary>
        public float DrawH { set; get; }

        /// <summary>
        /// 估计截面W宽度
        /// 用于绘图
        /// </summary>
        public float DrawW { set; get; }

        /// <summary>
        /// 纤维y轴坐标补偿
        /// 绘图使用
        /// </summary>
        public float offsety { set; get; }

        /// <summary>
        /// 纤维z轴坐标补偿
        /// 绘图使用
        /// </summary>
        public float offsetz { set; get; }

        #endregion

        /// <summary>
        /// 设定纤维组基本信息
        /// </summary>
        public void SetBasicInfo()
        {
            //初始化容器
            this.FiberGroupInfoList = new List<FiberGroup>();
            //z坐标列表
            var zList = this.FiberList.GetPropertyList("z").ConvertAll(f => (float)f);
            //y坐标列表
            var yList = this.FiberList.GetPropertyList("y").ConvertAll(f => (float)f);
            //半径列表
            var rList = this.FiberList.GetPropertyList("r").ConvertAll(f => (float)f);
            //遍历纤维列表：为不同材料创建纤维组
            this.FiberList.ForEach(fiber =>
            {
                //获得材料列表
                var matList = this.FiberGroupInfoList.GetPropertyList("MatID").ConvertAll(f => (int)f);
                //是否存在该材料
                if (!matList.Contains(fiber.MatID))
                    this.FiberGroupInfoList.Add(new FiberGroup(fiber.MatID, this.FiberList));
            });
            //纤维单元组按转动惯量排序
            this.FiberGroupInfoList.Sort();
            //估计截面长度及宽度
            this.DrawH = zList.Max() - zList.Min() + 2 * rList.Max();
            this.DrawW = yList.Max() - yList.Min() + 2 * rList.Max();
            //坐标补偿值
            this.offsety = yList.Min() - rList.Max();
            this.offsetz = zList.Max() + rList.Max();
            //设定纤维组颜色
            for(int i = 0; i < FiberGroupInfoList.Count; i++)
            {
                //获得颜色
                Color color = i < this.ColorList.Count? this.ColorList[i]:
                    this.GetRandomColor();
                //对应材料编号的列表并赋予颜色
                FiberList.Find("MatID", FiberGroupInfoList[i].MatID).ForEach(fiber => fiber.Color = color);
            }
        }

        /// <summary>
        /// 获得纤维描述
        /// </summary>
        /// <returns></returns>
        public List<string> GetFiberDescp()
        {
            //定义纤维列表
            var fiberList = new List<string>();
            //遍历纤维
            this.FiberList.ForEach(fiber =>
            {
                string str = "fiber ";
                str += fiber.y.ToString("f2") + " ";
                str += fiber.z.ToString("f2") + " ";
                str += fiber.A.ToString("f0") + " ";
                str += fiber.MatID.ToString() + " ";
                fiberList.Add(str);
            });
            //返回纤维列表
            return fiberList;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="str"></param>
        /// <param name="sr"></param>
        public SectionInfo(List<string> txtList, ref int index)
        {             
            //纤维列表
            this.FiberList = new List<FiberInfo>();
            //原始纤维命令
            this.OriFiberInfoList = new List<string>();
            //颜色列表
            this.ColorList = new List<Color> { Color.LightBlue, Color.Gold, Color.BlueViolet };
            //拆分语句
            List<string> strList = WilsonHelper.SplitStr(txtList[index++]);
            //读取截面编号
            this.SectionName = strList[2];
            //继续读取文件
            for(; index < txtList.Count; index++)
            {
                //防止为标注的语句
                if (txtList[index].Contains("#")) continue;
                //储存信息
                this.OriFiberInfoList.Add(txtList[index]);
                //获得纤维信息
                this.SetFiberList(txtList[index]);
                //判断截面定义终点
                if (txtList[index].Contains("}") && !txtList[index].Contains("#"))
                    break;
            }
            //计算截面基本信息
            this.SetBasicInfo();
        }

        /// <summary>
        /// 构造函数，用于flag2文件
        /// </summary>
        /// <param name="txtList"></param>
        /// <param name="index"></param>
        /// <param name="sectiontag"></param>
        public SectionInfo(List<string> txtList, ref int index,int sectiontag)
        {

        }
    }

    /// <summary>
    /// 截面信息
    /// </summary>
    public partial class SectionInfo
    {
        #region 绘图方法

        /// <summary>
        /// 绘图
        /// </summary>
        /// <param name="picBox"></param>
        public Graphics ToGraphics(PictureBox picBox, bool isAllShow)
        {
            //获得缩放与绘图补偿
            var scaleTuple = this.GetScaleAndOffset(picBox);
            //获得Graphics
            Graphics g = picBox.CreateGraphics();
            //清空图纸
            g.Clear(Color.WhiteSmoke);
            //全部显示则修改状态
            if (isAllShow) this.FiberGroupInfoList.ForEach(fiberGroup => fiberGroup.isVisible = true);
            //遍历纤维组
            for (int i = 0; i < this.FiberGroupInfoList.Count; i++)
            {
                //选择性绘图
                if (!this.FiberGroupInfoList[i].isVisible) continue;
                //筛选纤维组并绘制
                this.FiberList.Find("MatID", FiberGroupInfoList[i].MatID).ForEach(fiber =>
                fiber.ToGraphis(g, i, this.offsety, this.offsetz, scaleTuple));
            }
            return g;
        }

        /// <summary>
        /// 高亮显示选择的纤维
        /// </summary>
        /// <param name="picBox"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public bool HighLightFiber(PictureBox picBox, float x, float y, TextBox yText, TextBox zText, TextBox aText)
        {
            //获得选择的纤维
            var fiber = this.GetFiber(picBox, x, y);
            //判断是否为空
            if (fiber == null) return false;
            //绘制截面
            var g = this.ToGraphics(picBox, false);
            //纤维存在则高亮显示
            fiber.ToGraphis(g, this.offsety, this.offsetz, this.GetScaleAndOffset(picBox));
            //修改文本信息
            yText.Text = fiber.y.ToString("f2");
            zText.Text = fiber.z.ToString("f2");
            aText.Text = fiber.A.ToString("f0");
            return true;
        }

        /// <summary>
        /// 获得绘图缩放与绘图补偿
        /// </summary>
        /// <param name="picBox"></param>
        /// <returns></returns>
        private Tuple<float, float, float> GetScaleAndOffset(PictureBox picBox)
        {
            //截面缩小比例
            float scale = (float)Math.Min(0.9 * picBox.Size.Height
                / this.DrawH, 0.9 * picBox.Size.Width / this.DrawW);
            //坐标补偿
            float offsetPicX = (picBox.Size.Width - scale * this.DrawW) * 0.5F;
            float offsetPicY = (picBox.Size.Height - scale * this.DrawH) * 0.5F;
            //返回值
            return Tuple.Create(scale, offsetPicX, offsetPicY);
        }

        /// <summary>
        /// 获得鼠标点击最接近的纤维
        /// </summary>
        /// <param name="picBox"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        private FiberInfo GetFiber(PictureBox picBox, float x, float y)
        {
            //创建字典以获得最接近的纤维
            var dict = new SortedDictionary<float, FiberInfo>();
            //获得坐标转换基本参数
            var offsetTuple = this.GetScaleAndOffset(picBox);
            //初始化坐标误差
            float posError = new float();
            //遍历纤维
            this.FiberList.ForEach(fiber =>
            {
                //判断纤维是否在范围内
                if(fiber.isSelectFiber(x, y, this.offsety, this.offsetz, offsetTuple, ref posError))
                {
                    //判断键值是否存在
                    if (!dict.ContainsKey(posError)) dict.Add(posError, fiber);
                    //存在则取小半径纤维
                    else dict[posError] = dict[posError].r < fiber.r ? dict[posError] : fiber;
                }
            });
            //返回最接近的纤维
            return dict.Count != 0 ? dict.First().Value: null;
        }

        /// <summary>
        /// 产生随机颜色
        /// </summary>
        /// <returns></returns>
        private System.Drawing.Color GetRandomColor()
        {
            Random RandomNum_First = new Random((int)DateTime.Now.Ticks);
            //  对于C#的随机数，没什么好说的
            System.Threading.Thread.Sleep(RandomNum_First.Next(50));
            Random RandomNum_Sencond = new Random((int)DateTime.Now.Ticks);

            //  为了在白色背景上显示，尽量生成深色
            int int_Red = RandomNum_First.Next(256);
            int int_Green = RandomNum_Sencond.Next(256);
            int int_Blue = (int_Red + int_Green > 400) ? 0 : 400 - int_Red - int_Green;
            int_Blue = (int_Blue > 255) ? 255 : int_Blue;

            return System.Drawing.Color.FromArgb(int_Red, int_Green, int_Blue);
        }

        #endregion

        #region 构造纤维信息

        /// <summary>
        /// 获得纤维信息
        /// </summary>
        /// <param name="str_list"></param>
        /// <returns></returns>
        public void SetFiberList(string line)
        {
            //修剪语句(不用trim 避免有空白行)
            var strList = WilsonHelper.SplitStr(line);
            //Fiber：添加纤维信息
            if (strList.Count == 5 && line.Contains("fiber")) this.SetFiber(strList);
            //Patch
            else if (line.Contains("patch"))
            {
                //Rect
                if (line.Contains("rect") && strList.Count == 9) this.SetRectFiber(strList);
                //Circ
                else if (line.Contains("circ") && strList.Count == 11) this.SetCircleFiber(strList);
                //Quad
                else if (line.Contains("quad") && strList.Count == 13) this.SetQuadFiber(strList);
            }
            //Layer
            else if (line.Contains("layer"))
            {
                //straight
                if (line.Contains("straight") && strList.Count == 9) this.SetStraightFiber(strList);
                //circ
                else if (line.Contains("circ") && strList.Count >= 8)
                {
                    int mat_id = Convert.ToInt32(strList[2]);
                    int fiber_num = Convert.ToInt32(strList[3]);
                    float area = Convert.ToSingle(strList[4]);
                    float y_center = Convert.ToSingle(strList[5]);
                    float z_center = Convert.ToSingle(strList[6]);
                    float radius = Convert.ToSingle(strList[7]);
                    //Optional
                    if (strList.Count == 10)
                    {
                        float start_ang = Convert.ToSingle(strList[8]);
                        float end_ang = Convert.ToSingle(strList[9]);
                        this.SetCircleFiber(mat_id, fiber_num,
                            area, y_center, z_center, radius, start_ang, end_ang);
                    }
                    else if (strList.Count == 8)
                    {
                        this.SetCircleFiber(mat_id, fiber_num,
                            area, y_center, z_center, radius);
                    }
                }
            }
        }

        /// <summary>
        /// 读取纤维命令
        /// </summary>
        /// <param name="strList"></param>
        /// <returns></returns>
        private void SetFiber(List<string> strList)
        {
            //y轴坐标
            float y = Convert.ToSingle(strList[1]);
            //z轴坐标
            float z = Convert.ToSingle(strList[2]);
            //纤维面积
            float A = Convert.ToSingle(strList[3]);
            //纤维材料
            int Mat_id = Convert.ToInt32(strList[4]);
            //纤维信息
            this.FiberList.Add(new FiberInfo(Mat_id, y, z, A));
        }

        /// <summary>
        /// 读取Patch Rect纤维命令
        /// </summary>
        /// <param name="strList"></param>
        /// <returns></returns>
        private void SetRectFiber(List<string> strList)
        {
            //局部变量
            int mat_id = Convert.ToInt32(strList[2]);
            int divide_y = Convert.ToInt32(strList[3]);
            int divide_z = Convert.ToInt32(strList[4]);
            float I_y = Convert.ToSingle(strList[5]);
            float I_z = Convert.ToSingle(strList[6]);
            float J_y = Convert.ToSingle(strList[7]);
            float J_z = Convert.ToSingle(strList[8]);
            //对角线坐标增量
            float delta_z = (J_z - I_z) / divide_z;
            float delta_y = (J_y - I_y) / divide_y;
            float A_i = delta_y * delta_z;
            //先横后纵
            for (int zi = 1; zi <= divide_z; zi++)
            {
                //z方向坐标
                float z = I_z + (zi - 1) * delta_z + 0.5F * delta_z;
                for (int yi = 1; yi <= divide_y; yi++)
                {
                    //y方向坐标
                    float y = I_y + (yi - 1) * delta_y + 0.5F * delta_y;
                    //添加至列表
                    this.FiberList.Add(new FiberInfo(mat_id, y, z, A_i));
                }
            }
        }


        /// <summary>
        /// 读取Patch Quad纤维命令
        /// </summary>
        /// <param name="strList"></param>
        /// <returns></returns>
        private void SetQuadFiber(List<string> strList)
        {
            //局部变量
            int mat_id = Convert.ToInt32(strList[2]);
            int divide_IJ = Convert.ToInt32(strList[3]);
            int divide_IL = Convert.ToInt32(strList[4]);
            float I_y = Convert.ToSingle(strList[5]);
            float I_z = Convert.ToSingle(strList[6]);
            float J_y = Convert.ToSingle(strList[7]);
            float J_z = Convert.ToSingle(strList[8]);
            float K_y = Convert.ToSingle(strList[9]);
            float K_z = Convert.ToSingle(strList[10]);
            float L_y = Convert.ToSingle(strList[11]);
            float L_z = Convert.ToSingle(strList[12]);

            //先求LI和KJ边上点的坐标，再从I到L依次求解IJ方向上点的坐标
            for (int ILi = 1; ILi <= divide_IL; ILi++)
            {
                //求解LI和KJ边上的点坐标,及对应的方块的边点坐标
                float z1 = I_z + (ILi - 0.5F) * (L_z - I_z) / divide_IL;
                float y1 = I_y + (ILi - 0.5F) * (L_y - I_y) / divide_IL;
                float z2 = J_z + (ILi - 0.5F) * (K_z - J_z) / divide_IL;
                float y2 = J_y + (ILi - 0.5F) * (K_y - J_y) / divide_IL;
           
                for (int IJi = 1; IJi <= divide_IJ; IJi++)
                {
                    //从I到L依次求解IJ方向上点圆心的坐标
                    float y = y1 + (IJi - 0.5F) * (y2 - y1) / divide_IJ;
                    float z = z1 + (IJi - 0.5F) * (z2 - z1) / divide_IJ;

                    //求解小块四点沿着分隔边对应的边上的四点，逆时针分别为ail,,bjk,cjk,dil
                    float yail = y1 - 0.5F * (L_y - I_y) / divide_IL;
                    float zail = z1 - 0.5F * (L_z - I_z) / divide_IL;
                    float ybjk = y2 - 0.5F * (K_y - J_y) / divide_IL;
                    float zbjk = z2 - 0.5F * (K_z - J_z) / divide_IL;
                    float ydil = y1 + 0.5F * (L_y - I_y) / divide_IL;
                    float zdil = z1 + 0.5F * (L_z - I_z) / divide_IL;
                    float ycjk = y2 + 0.5F * (K_y - J_y) / divide_IL;
                    float zcjk = z2 + 0.5F * (K_z - J_z) / divide_IL;

                    //求解小块四点的坐标，依次为abcd点
                    float ya = yail + (IJi - 1) * (ybjk - yail) / divide_IJ;
                    float yb = yail + IJi * (ybjk - yail) / divide_IJ;
                    float za = zail + (IJi - 1) * (zbjk - zail) / divide_IJ;
                    float zb = zail + IJi * (zbjk - zail) / divide_IJ;
                    float yd = ydil + (IJi - 1) * (ycjk - ydil) / divide_IJ;
                    float yc = ydil + IJi * (ycjk - ydil) / divide_IJ;
                    float zd = zdil + (IJi - 1) * (zcjk - zdil) / divide_IJ;
                    float zc = zdil + IJi * (zcjk - zdil) / divide_IJ;

                    //求解方块的边长及对角线长度
                    double ab = Math.Sqrt(Math.Pow((ya - yb), 2) + Math.Pow((za - zb), 2));
                    double ac = Math.Sqrt(Math.Pow((ya - yc), 2) + Math.Pow((za - zc), 2));
                    double ad = Math.Sqrt(Math.Pow((ya - yd), 2) + Math.Pow((za - zd), 2));
                    double bc = Math.Sqrt(Math.Pow((yb - yc), 2) + Math.Pow((zb - zc), 2));
                    double bd = Math.Sqrt(Math.Pow((yb - yd), 2) + Math.Pow((zb - zd), 2));
                    double cd = Math.Sqrt(Math.Pow((yc - yd), 2) + Math.Pow((zc - zd), 2));
                    double D = Math.Abs(Math.Pow(ab, 2) + Math.Pow(cd, 2) - Math.Pow(ad, 2) - Math.Pow(bc, 2));

                    //求解面积
                    float A_i = (float)(0.25 * Math.Sqrt(Math.Pow((2 * ac * bd), 2) - Math.Pow(D, 2)));
                    this.FiberList.Add(new FiberInfo(mat_id, y, z, A_i));
                }

            }
        }




        /// <summary>
        /// 读取Layer Straight纤维命令
        /// </summary>
        /// <param name="strList"></param>
        /// <returns></returns>
        private void SetStraightFiber(List<string> strList)
        {
            //局部变量
            int mat_id = Convert.ToInt32(strList[2]);
            int num_fiber = Convert.ToInt32(strList[3]);
            float ai = Convert.ToSingle(strList[4]);
            float y_start = Convert.ToSingle(strList[5]);
            float z_start = Convert.ToSingle(strList[6]);
            float y_end = Convert.ToSingle(strList[7]);
            float z_end = Convert.ToSingle(strList[8]);

            //坐标增量
            float delta_y = (y_end - y_start) / (num_fiber - 1);
            float delta_z = (z_end - z_start) / (num_fiber - 1);

            for (int i = 1; i <= num_fiber; i++)
            {
                //计算坐标
                float y = y_start + (i - 1) * delta_y;
                float z = z_start + (i - 1) * delta_z;
                //添加至列表
                this.FiberList.Add(new FiberInfo(mat_id, y, z, ai));
            }
        }

        /// <summary>
        /// 读取Patch Cric纤维命令
        /// </summary>
        /// <param name="strList"></param>
        /// <returns></returns>
        private void SetCircleFiber(List<string> strList)
        {
            //局部变量
            int mat_id = Convert.ToInt32(strList[2]);
            int divide_circ = Convert.ToInt32(strList[3]);
            int divide_rad = Convert.ToInt32(strList[4]);
            float center_y = Convert.ToSingle(strList[5]);
            float center_z = Convert.ToSingle(strList[6]);
            float int_r = Convert.ToSingle(strList[7]);
            float ext_r = Convert.ToSingle(strList[8]);
            float start_ang = Convert.ToSingle(strList[9]);
            float end_ang = Convert.ToSingle(strList[10]);

            //径向增量
            float delta_r = (ext_r - int_r) / divide_rad;
            //往径向外拓
            for (int ri = 1; ri <= divide_rad; ri++)
            {
                //内径
                float inner_r = int_r + (ri - 1) * delta_r;
                //外径
                float outer_r = inner_r + delta_r;
                //圆环中心半径
                float r = (inner_r + outer_r) * 0.5F;
                //外圆面积
                float outer_a = (float)(Math.Pow((double)outer_r, 2) * Math.PI);
                //内圆面积
                float inner_a = (float)(Math.Pow((double)inner_r, 2) * Math.PI);
                //每个纤维面积
                float Ai = (outer_a - inner_a) * (end_ang - start_ang) / 360F / divide_circ;
                //添加纤维
                this.SetCircleFiber(mat_id, divide_circ, Ai, center_y, center_z, r, start_ang, end_ang);
            }
        }

        /// <summary>
        /// 环形纤维
        /// </summary>
        /// <param name="matid"></param>
        /// <param name="fiberNum"></param>
        /// <param name="area"></param>
        /// <param name="yCenter"></param>
        /// <param name="zCenter"></param>
        /// <param name="r"></param>
        /// <param name="startAng"></param>
        /// <param name="endAng"></param>
        /// <returns></returns>
        private void SetCircleFiber(int matid, int fiberNum, float area,
            float yCenter, float zCenter, float r, float startAng = 0F, float endAng = 360F)
        {
            //初始化容器
            List<FiberInfo> fiber_list = new List<FiberInfo>();
            //转角增量
            float delta_angle = (endAng - startAng) / fiberNum;
            //绕圆心转动
            for (int angle_i = 1; angle_i <= fiberNum; angle_i++)
            {
                //角度
                float angle = startAng + (angle_i - 1) * delta_angle;
                //占比
                float ratio = angle / 360F;
                //纤维坐标
                float yi = (float)(r * Math.Sin(0.5 * Math.PI - Math.PI * ratio * 2)) + yCenter;
                float zi = (float)(r * Math.Cos(0.5 * Math.PI - Math.PI * ratio * 2)) + zCenter;
                //添加至列表
                this.FiberList.Add(new FiberInfo(matid, yi, zi, area));
            }
        }

        #endregion
    }

    /// <summary>
    /// OpenSEES纤维信息
    /// </summary>
    class SectionHelper
    {
        /// <summary>
        /// 获得截面字典
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static List<SectionInfo> GetSections(string path)
        {
            //初始化
            var sectionList = new List<SectionInfo>();
            //读取文件所有讯息
            var txtList = File.ReadLines(path).ToList();
            //判断是否是tcl模型文件
            bool is_tcl = new bool();
            //正则表达式
            Regex rgx1 = new Regex(@"section\s+Fiber\s+\d+\s+\{");
            Regex rgx2 = new Regex(@"FiberSection3d,\s*tag:\s*(\d+)");
            for (int index = 0; index < txtList.Count; index++)
            {
                if (rgx1.IsMatch(txtList[index]))
                {
                    is_tcl = true;
                    break;
                }
                if (rgx2.IsMatch(txtList[index]))
                {
                    is_tcl = false;
                    break;
                }
            }
            if (is_tcl)
            {
                //遍历文件
                for (int index = 0; index < txtList.Count; index++)
                {
                    //非纤维命令
                    if (txtList[index].Contains("#") || !txtList[index].Contains("section") ||
                        !txtList[index].Contains("Fiber") || !txtList[index].Contains("{"))
                        continue;
                    //获得截面信息
                    sectionList.Add(new SectionInfo(txtList, ref index));
                }
            }
            else
            {
                //截面编号列表
                List<int> taglist = new List<int>();
                //遍历文件
                for (int index = 0; index < txtList.Count; index++)
                {
                    //非截面命令
                    if (!rgx2.IsMatch(txtList[index])) continue;
                    //截面编号
                    Match mt = rgx2.Match(txtList[index]);
                    int tag = Convert.ToInt32(rgx2.Match(txtList[index]).Groups[1].Value);
                    //如果此截面已定义
                    if (taglist.Contains(tag)) continue;
                    //获得截面信息
                    sectionList.Add(new SectionInfo(txtList, ref index, tag));
                }
            }
            
            //返回
            return sectionList;
        }


    }
}
